package org.tri.persistence;

import java.util.Collection;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.util.HibernateUtil;

/**
 * La clase {@code PersistenceManager} es la encargada de manejar la persistencia o 
 * eliminación de las entidades de la aplicación a la base de datos.
 * @author kiira
 * @version 1.0, Nov 19, 2011
 */
public class PersistenceManager {

    /**
     * Persiste una entidad en la base de datos, ya sea insertando la información
     * del nuevo objeto, o actualizando un objeto antigüo.
     * @param o objeto a persistir.
     * @throws HibernateException si ocurre un error en la transacción.
     */
    public static void makePersistent( Object o ) throws HibernateException {
        //obtenemos la sesion 
        Session sesion = HibernateUtil.getCurrentSession();
       
        //persistimos el objeto
        synchronized ( sesion ) {
            try {
                sesion.beginTransaction();
                sesion.saveOrUpdate( o );
                sesion.getTransaction().commit();

            } catch ( HibernateException hE ) {
                sesion.getTransaction().rollback();

                sesion.close();

                throw hE;

            }

        }//end syncrhonized block

    }//end método makePersistent

    /**
     * Elimina un objeto persistente de la base de datos.
     * @param o objeto a eliminar.
     * @throws HibernateException si ocurre un error en la transacción.
     */
    public static void makeTransient( Object o ) throws HibernateException {
        //obtenemos la sesion 
        Session sesion = HibernateUtil.getCurrentSession();

        //eliminamos el objeto
        synchronized ( sesion ) {
            try {
                sesion.beginTransaction();
                sesion.delete( o );
                sesion.getTransaction().commit();

            } catch ( HibernateException hE ) {
                sesion.getTransaction().rollback();

                sesion.close();

                throw hE;

            }

        }//end syncrhonized block
        
    }//end método makeTransient

    /**
     * Refresca un objeto en el cual alguna de sus propiedades "dinámicas" 
     * (asociaciones, formulas, etc) pudo haber cambiado dada una modificación
     * en la base de datos.
     * @param o objeto a refrescar.
     * @throws HibernateException si ocurre un error en la transacción.
     */
    public static void refresh( Object o ) throws HibernateException {
        //obtenemos la sesion 
        Session sesion = HibernateUtil.getCurrentSession();

        //actualizamos el objeto
        synchronized ( sesion ) {
            try {
                sesion.beginTransaction();
                sesion.refresh( o );
                sesion.getTransaction().commit();

            } catch ( HibernateException hE ) {
                sesion.getTransaction().rollback();

                sesion.close();

                throw hE;

            }
        }//end syncrhonized block

    }//end método refresh

    /**
     * Une el estado de una entidad "detached" por alguna razon (sesión perdida)
     * con el estado de la base de datos para evitar inconsistencias
     * @param <T> clase del objeto a unir.
     * @param o objeto a unir.
     * @return objeto "nuevo" que corresponde a la fusión de estados.
     * @throws HibernateException si ocurre un error en la transacción.
     */
    public static <T> T merge( T o ) throws HibernateException {
        //obtenemos la sesion 
        Session sesion = HibernateUtil.getCurrentSession();

        //hacemos el merge del objeto
        synchronized ( sesion ) {
            try {
                sesion.beginTransaction();
                o = (T)sesion.merge( o );
                sesion.getTransaction().commit();

            } catch ( HibernateException hE ) {
                sesion.getTransaction().rollback();

                sesion.close();

                throw hE;

            }

        }//end syncrhonized block

        return o;

    }//end método merge
    
    /**
     * Permite ejecutar una query SQL (DML) si se precisa realizar una tarea 
     * con mayor performance que utilizando hibernate.
     * @param query query a ejecutar.
     */
    public static void executeQuery( String query ) {
        //obtenemos la sesion 
        Session sesion = HibernateUtil.getCurrentSession();

        //ejecutamos la query
        synchronized ( sesion ) {
            try {
                sesion.beginTransaction();
                sesion.createSQLQuery( query ).executeUpdate();
                sesion.getTransaction().commit();

            } catch ( HibernateException hE ) {
                sesion.getTransaction().rollback();

                sesion.close();

                throw hE;

            }

        }//end syncrhonized block
        
    }//end método executeQuery
    
    /**
     * Elimina el objeto pasado por parámetros del cache de objetos de la
     * sesión, de manera que el mismo no ocupa lugar en memoria, pero si
     * desea volver a ser usado, hay que volver a recuperarlo de la db.
     * @param o objeto a evictear.
     * @throws HibernateException si ocurre un error al evictear el objeto.
     */
    public static void evict( Object o ) throws HibernateException {
        //obtenemos la sesion 
        Session sesion = HibernateUtil.getCurrentSession();

        //evictiamos el objeto de la sesion
        synchronized ( sesion ) {
            try {
                sesion.beginTransaction();
                sesion.evict( o );
                sesion.getTransaction().commit();

            } catch ( HibernateException hE ) {
                sesion.getTransaction().rollback();

                sesion.close();

                throw hE;

            }

        }//end syncrhonized block
        
    }//end método evict
    
    /**
     * Permite evictear un grupo de objetos en una operación atómica y 
     * sincrónica.
     * @param objects objetos a evictear.
     * @throws HibernateException si ocurre un error mientras se evictea.
     */
    public static void evict( Collection objects) throws HibernateException {        
        //obtenemos la sesion 
        Session sesion = HibernateUtil.getCurrentSession();

        //evictiamos el objeto de la sesion
        synchronized ( sesion ) {
            try {
                sesion.beginTransaction();
                
                for ( Object o : objects )
                    sesion.evict( o );
                
                sesion.getTransaction().commit();

            } catch ( HibernateException hE ) {
                sesion.getTransaction().rollback();

                sesion.close();

                throw hE;

            }

        }//end syncrhonized block
        
    }//end método evict

}//end clase PersistenceManager

